package com.awenhui.demo.log;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.json.async.NonBlockingJsonParser;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.security.reactive.ApplicationContextServerWebExchangeMatcher;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author heliuhui
 * @version 1.0
 * @date 2021/4/9 15:05
 */
@Aspect
public abstract class AbstractLogAop {
    private Logger logger = LoggerFactory.getLogger(getClass());

    //业务参数在参数中的名称
    public static final String BUSI_PARAM_NAME = "busi_id";

    /**
     * controller层的切点
     *
     * @author lv。
     */
    @Pointcut("@annotation(com.awenhui.demo.log.LogOperation)")
    public void logAspect() {

    }

    @AfterReturning(value = "logAspect()", returning = "result")
    public void doAfferReturning(JoinPoint joinPoint, Object result) throws NoSuchFieldException, IllegalAccessException {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String url = request.getRequestURL().toString();
        String userAgent = request.getHeader("user-agent");
        //获取切入点所在的方法
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        LogOperation logOperation = method.getAnnotation(LogOperation.class);
        //获取参数信息
        Map<String, Object> params = getParams(logOperation, joinPoint);
        //处理结果
        if (!logOperation.resultSerializable()) {
            result = null;
        }

        //获取用户信息
        String userName = getUserName(request);
        //获取ip
        String ip = getRemoteHost(request);
        LogDto logDto = new LogDto();

        logDto.setModule(logOperation.module());
        logDto.setAction(logOperation.action());
        if(params.containsKey(BUSI_PARAM_NAME))
        {
            logDto.setBusiId(params.get(BUSI_PARAM_NAME));
            params.remove(BUSI_PARAM_NAME);
        }
        logDto.setIp(ip);
        logDto.setUrl(url);
        logDto.setUseAgent(userAgent);
        logDto.setUserName(userName);
        logDto.setArgs(serializeParams(params));
        logDto.setResult(serializeResult(result));
        //保存日志
        saveLog(logDto, logOperation, joinPoint, request);
    }


    /**
     * 获取参数
     *
     * @param logOperation
     * @param joinPoint
     * @return
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    protected Map<String, Object> getParams(LogOperation logOperation, JoinPoint joinPoint) throws NoSuchFieldException, IllegalAccessException {
        //获取参数
        Object[] args = joinPoint.getArgs();
        String[] parameterNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
        Map<String, Object> params = new HashMap<>();
        //记录参数值
        if (args != null && args.length > 0) {

            for (int i = 0; i < args.length; i++) {
                Object value = args[i];
                //如果是文件对象
                if (value instanceof MultipartFile) {
                    MultipartFile file = (MultipartFile) args[i];
                    value = file.getOriginalFilename();  //获取文件名
                }
                //如果是HttpServletRequest和HttpServletResponse对象则获取字符串
                if (value instanceof HttpServletRequest||value instanceof HttpServletResponse) {
                    value= value.getClass().getTypeName();
                }
                params.put(parameterNames[i], value);
            }
        }
        //移除不能序列化的参数
        if (logOperation.excluedeArgs().length > 0) {
            for (String eArgs : logOperation.excluedeArgs()) {
                params.remove(eArgs);
            }
        }
        //记录业务参数
        if (logOperation.busiParamName() != null && !logOperation.busiParamName().isEmpty()) {
            if (!logOperation.busiParamName().contains(".")) {
                params.put(BUSI_PARAM_NAME, params.get(logOperation.busiParamName()));
            } else {
                String[] objArgName = logOperation.busiParamName().split("\\.");
                Object objArg = params.get(objArgName[0]);
                Field declaredField = objArg.getClass().getDeclaredField(objArgName[1]);
                declaredField.setAccessible(true);
                Object objArgValue = declaredField.get(objArg);
                params.put(BUSI_PARAM_NAME, objArgValue);
            }
        }
        return params;
    }

    /**
     * 序列化参数为字符串
     *
     * @param params
     * @return
     */
    protected String serializeParams(Map<String, Object> params) {
        if(params==null||params.size()==0)return null;
        String strParams = JSONObject.toJSONString(params);
        return strParams;
    }

    /**
     * 序列化返回值为字符串
     *
     * @param result
     * @return
     */
    protected String serializeResult(Object result) {
        if(result==null)return null;
        String strResult = JSONObject.toJSONString(result);
        return strResult;
    }



    /**
     * 获取登录用户信息
     * 如果需要获取则在子类实现
     *
     * @param request
     * @return
     */
    protected String getUserName(HttpServletRequest request) {
        return "AbstractUser";
    }

    /**
     * 子类实现保存日志的方法
     *
     * @param logDto
     * @param logOperation
     * @param joinPoint
     * @param request
     */
    public abstract void saveLog(LogDto logDto, LogOperation logOperation, JoinPoint joinPoint, HttpServletRequest request);

    /**
     * 获取ip地址
     *
     * @param request
     * @return
     */
    public String getRemoteHost(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }





}
